#ifndef _SE_RENDERAPI_
#define _SE_RENDERAPI_

#include "Render/RenderContext.h"

namespace SE
{
	enum class RenderAPIType
	{
		OpenGL
	};

	class RenderAPI
	{
	protected:
		Weak<RenderContext> mCurrentContext;

    public:
        enum class EnabledTest : U8
        {
            None        = 0,
            Blend       = BIT(0),
            Depth       = BIT(1),
            Color       = BIT(2),
            Default     = Depth | Color
        };

	public:
		RenderAPI() = default;
		virtual ~RenderAPI() = default;

        static Ref<RenderContext> CreateContext();
        static void DestoryContext(const Ref<RenderContext>& context);

		static Ref<RenderAPI> Create(const RenderAPIType type);

    public:
        static RenderAPIType GetAPIType();

        static void MakeContextCurrent(const Ref<RenderContext>& context);

        static void SetViewport(
            const U32 x,
            const U32 y,
            const U32 width,
            const U32 height
        );
        static void SetClearColor(const Vector4f& color);
        static void SetLineWidth(const Float width);

        static void Clear();

        static void BindToBackground();
        static void BindToForeground();

        static Ref<FrameBuffer> GetBackground();
        static Vector2i GetBackgroundSize();
        static void ResizeBackground(const U32 width, const U32 height);

        static void BeginScene(const CameraData cameraData);
        static void EndScene();

        static void StartBatch();
        static void NextBatch();
        static void Flush();
        static void ResetStats();

        static void EnableTests(const EnabledTest enabledTests);
        static void DisableTests(const EnabledTest enabledTests);

        /// <summary>
        /// The most basic interface to submit triangle list for rendering.
        /// </summary>
        /// <param name="vertices"> The header pointer of 'RenderVertex' list. </param>
        /// <param name="vertexCount"> The COUNT of 'RenderVertex' element. </param>
        /// <param name="indices"> The header pointer of 'U32' list. </param>
        /// <param name="indexCount"> The COUNT of 'U32' element. </param>
        static void SubmitTriangles(
            RenderVertex*   vertices,
            const U32       vertexCount,
            U32*            indices,
            const U32       indexCount);

        static void SubmitTriangles(
            const RenderVertex2D*   vertices,
            const U32               triangleCount,
            const Matrix4f&         transform,
            const Ref<Texture>&     texture,
            const Float             tilingFactor    = 1.0f,
            const I32               entityID        = -1);

        static void SubmitTriangles(
            const RenderVertex3D*   vertices,
            const U32               triangleCount,
            const Matrix4f&         transform,
            const Ref<Texture>&     texture,
            const Float             tilingFactor    = 1.0f,
            const I32               entityID        = -1);

        static void SubmitQuads(
            const RenderVertex2D*   vertices,
            const U32               quadCount,
            const Matrix4f&         transform,
            const Ref<Texture>&     texture,
            const Float             tilingFactor    = 1.0f,
            const I32               entityID        = -1);

        static void SubmitQuads(
            const RenderVertex3D*   vertices,
            const U32               quadCount,
            const Matrix4f&         transform,
            const Ref<Texture>&     texture,
            const Float             tilingFactor    = 1.0f,
            const I32               entityID        = -1);

	protected:
        virtual RenderAPIType getAPIType() const = 0;

        virtual void makeContextCurrent(const Ref<RenderContext>& context) = 0;

        virtual void setViewport(
            const U32 x,
            const U32 y,
            const U32 width,
            const U32 height) = 0;
        virtual void setClearColor(const Vector4f& color) = 0;
        virtual void setLineWidth(const Float width) = 0;

        virtual void clear() = 0;

        virtual void beginScene(const CameraData cameraData) = 0;
        virtual void endScene() = 0;

        virtual void startBatch() = 0;
        virtual void nextBatch() = 0;
        virtual void flush() = 0;
        virtual void resetStats() = 0;

        virtual void enableTests(const EnabledTest enabledTests) = 0;
        virtual void disableTests(const EnabledTest enabledTests) = 0;

        virtual void drawIndexed(const Ref<VertexArray>& vertexArray, const U64 indexCount) = 0;
        virtual void drawArrays(const Ref<VertexArray>& vertexArray, const U64 vertexCount) = 0;
        virtual void drawLines(const Ref<VertexArray>& vertexArray, const U64 vertexCount) = 0;

        virtual void submitTriangles(
            RenderVertex*   vertices,
            const U32       vertexCount,
            U32*            indices,
            const U32       indexCount) = 0;

        virtual void submitTriangles(
            const RenderVertex2D*   vertices,
            const U32               triangleCount,
            const Matrix4f&         transform,
            const Ref<Texture>&     texture,
            const Float             tilingFactor,
            const I32               entityID) = 0;

        virtual void submitTriangles(
            const RenderVertex3D*   vertices,
            const U32               triangleCount,
            const Matrix4f&         transform,
            const Ref<Texture>&     texture,
            const Float             tilingFactor,
            const I32               entityID) = 0;

        virtual void submitQuads(
            const RenderVertex2D*   vertices,
            const U32               quadCount,
            const Matrix4f&         transform,
            const Ref<Texture>&     texture,
            const Float             tilingFactor,
            const I32               entityID) = 0;

        virtual void submitQuads(
            const RenderVertex3D*   vertices,
            const U32               quadCount,
            const Matrix4f&         transform,
            const Ref<Texture>&     texture,
            const Float             tilingFactor,
            const I32               entityID) = 0;
	};

    BindEnumOperationAddToBoolean(RenderAPI::EnabledTest, U8)
} // !namespace SE

#endif // !_SE_RENDERAPI_